<template>
  <div class="box--picker" :class="boxClass">
    <div class="base-date-picker">
      <i v-if="!isSingleRowDisplay" class="box-icon--start"></i>
      <el-date-picker
        v-model="start"
        @change="handleDateChange"
        :type="type"
        :placeholder="`开始${isDateType ? '日期' : '时间'}`"
        :size="size"
        :format="format"
        :value-format="format"
        :clearable="true"
        :disabledDate="startDisabledDate"
      >
      </el-date-picker>
    </div>
    <span v-if="isSingleRowDisplay && isRange" class="box-divider">-</span>
    <div class="base-date-picker" v-if="isRange">
      <i v-if="!isSingleRowDisplay" class="box-icon--end"></i>
      <el-date-picker
        v-model="end"
        @change="handleDateChange"
        :type="type"
        :placeholder="`结束${isDateType ? '日期' : '时间'}`"
        :size="size"
        :format="format"
        :value-format="format"
        :clearable="true"
        :disabledDate="endDisabledDate"
      >
      </el-date-picker>
    </div>
  </div>
</template>

<script setup>
  import tool from '@/utils/tool';
  import {ref, toRef, reactive, computed, watch, onMounted, onUnmounted, getCurrentInstance} from 'vue';
  const props = defineProps({
    // 尺寸
    size: {
      type: String,
      default: 'mini'
    },
    // 类型 date datetime
    type: {
      type: String,
      default: 'datetime'
    },
    // 是否需要选择范围
    isRange: {
      type: Boolean,
      default: false
    },
    // 初始化的时间的值，如果是范围 就传入一个数组
    value: {
      type: [String, Array, Number],
      default: ''
    },
    // 日期选择器返回值类型：string integer arrayString arrayInteger
    datePickerValueType: {
      type: String,
      default: 'string'
    },
    // 是否单行展示，针对时间范围选择
    isSingleRowDisplay: {
      type: Boolean,
      default: true
    },
    // 是否是可折叠下拉框
    isDropdown: {
      type: Boolean,
      default: false
    },
    // 可选的最小日期
    minDate: {
      type: String,
      default: '2020-01-01'
    },
    // 可选的最大日期
    maxDate: {
      type: String,
      default: tool.formatDate(new Date(), 'YYYY-MM-DD')
    }
  });

  const {proxy} = getCurrentInstance();
  const initValue = toRef(props, 'value');
  const emit = defineEmits(['change']);
  const timeRange = ref([' 00:00:00', ' 23:59:59']);
  const start = ref('');
  const end = ref('');
  const timestamp = reactive({
    start: 0,
    startDate: 0,
    end: 0,
    endDate: 0
  });

  const isDateType = computed(() => props.type === 'date');
  const boxClass = computed(() => {
    return {
      flexdc: !props.isSingleRowDisplay,
      date: props.isDateType,
      dropdown: props.isDropdown
    };
  });

  const format = computed(() => isDateType.value ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss');
  const formatDateType = computed(() => isDateType.value ? 'YYYY-MM-DD' : 'YYYY-MM-DD hh:mm:ss');
  const minTimestamp = computed(() => new Date(props.minDate + timeRange.value[0]).getTime());
  const maxTimestamp = computed(() => new Date(props.maxDate + timeRange.value[1]).getTime());

  // 根据时间选择器标识，简单设置初始化的时间戳。
  const getInitTimestamp = (flag = 'start') => {
    if (flag === 'start') {
      return {
        dateTime: minTimestamp.value,
        date: minTimestamp.value
      };
    }
    return {
      dateTime: maxTimestamp.value,
      date: new Date(tool.formatDate(maxTimestamp.value, 'YYYY-MM-DD')).getTime()
    };
  };

  // 根据时间选择器标识，设置该时间选择器的时间戳
  const setTimestamp = (flag = 'start') => {
    const dateStr = proxy[flag];
    const timestampAll = dateStr ? getTimestamp(flag) : getInitTimestamp(flag);
    timestamp[flag] = timestampAll.dateTime;
    timestamp[flag + 'Date'] = timestampAll.date;
  };

  // 根据时间选择器标识，获得该时间选择器的时间戳
  const getTimestamp = (flag = 'start') => {
    const dateStr = proxy[flag];
    return {
      dateTime: new Date(
        isDateType.value ? dateStr + timeRange.value[flag === 'end' ? 0 : 1] : dateStr
      ).getTime(),
      date: new Date(dateStr.replace(/(?<=\s)\S*/g, '').trim() + timeRange.value[0]).getTime()
    };
  };

  // 拿到时间选择器的值，type表示值类型：string integer arrayString arrayInteger
  const getDatePickerValue = (type = props.datePickerValueType) => {
    if (!props.isRange) {
      const value = isDateType.value ? timestamp.startDate : timestamp.start;
      return type === 'string' ? start.value : start.value && value;
    }
    // 范围
    if (type === 'string') return start.value + (start.value && end.value && '/') + end.value;
    const timestampRange = isDateType.value
      ? [timestamp.startDate, timestamp.endDate]
      : [timestamp.start, timestamp.end];
    return type === 'arrayInteger' ? timestampRange : [start.value, end.value];
  };

  const startDisabledDate = (time) => {
    // time值是当前的日期
    const dateTimestamp = time.getTime();
    return (
      dateTimestamp < minTimestamp.value ||
      dateTimestamp > maxTimestamp.value ||
      (timestamp.endDate && dateTimestamp > timestamp.endDate)
    );
  };
  const endDisabledDate = (time) => {
    const dateTimestamp = time.getTime();
    return (
      dateTimestamp < minTimestamp.value ||
      dateTimestamp > maxTimestamp.value ||
      dateTimestamp < timestamp.startDate
    );
  };

  // 选择日期和时间后要根据用户选择的时间合理调整选择结果
  const setDateTimeValue = (flag = 'start') => {
    const now = {
      time: tool.formatDate(Date.now(), 'hh:mm:ss'),
      date: tool.formatDate(Date.now(), 'YYYY-MM-DD')
    };
    const min = {};
    // 如果最小日期是当天 那么最小的时间范围设置为当前时间
    min.timestamp = props.minDate === now.date ? Date.now() : minTimestamp.value;
    min.datetime = tool.formatDate(min.timestamp, formatDateType.value);
    const max = {};
    // 如果最大日期是当天 那么最大的时间范围设置为当前时间
    max.timestamp = props.maxDate === now.date ? Date.now() : maxTimestamp.value;
    max.datetime = tool.formatDate(max.timestamp, formatDateType.value);

    if (timestamp[flag] < min.timestamp) {
      proxy[flag] = min.datetime;
      timestamp[flag] = min.timestamp;
    }
    if (timestamp[flag] > max.timestamp) {
      proxy[flag] = max.datetime;
      timestamp[flag] = max.timestamp;
    }
  };

  const handleDateChange = () => {
    start.value = start.value || '';
    end.value = end.value || '';
    // 日期时间选择器 时间范围限制
    if (!isDateType.value) {
      setDateTimeValue('start');
      setDateTimeValue('end');
      if (end.value && (timestamp.start > timestamp.end)) {
        start.value = tool.formatDate(timestamp.end, formatDateType.value);
        timestamp.start = timestamp.end;
      }
    }
    emit('change', getDatePickerValue(props.valueType));
  };

  const clearDate = () => {
    start.value = '';
    end.value = '';
  };

  const handleClick = (event) => {
    let elem = event.target || event.srcElement;
    while (elem) {
      if (elem.classList && Array.from(elem.classList).includes('dropdown')) return false;
      elem = elem.parentNode;
    }
  };

  // 有值时初始化
  const init = () => {
    const value = initValue.value;
    if (value instanceof Array) {
      start.value = Number.isInteger(value[0]) ? tool.formatDate(value[0], formatDateType.value) : value[0];
      end.value = Number.isInteger(value[1]) ? tool.formatDate(value[1], formatDateType.value) : value[1];
    } else if (value && typeof value === 'number') {
      start.value = tool.formatDate(value, formatDateType.value);
    } else {
      start.value = value;
    }
    setTimestamp('start');
    setTimestamp('end');
  }

  watch(start, () => setTimestamp('start'));
  watch(end, () => setTimestamp('end'));
  watch(initValue, init);

  onMounted(() => {
    init();
    props.isDropdown && document.addEventListener('click', handleClick);
  });

  onUnmounted(() => {
    props.isDropdown && document.removeEventListener('click', handleClick);
  });

  defineExpose({clearDate});
</script>

<style scoped lang="less">
  .box--picker {
    .dflex;
    &.flexdc {
      flex-direction: column;
      .base-date-picker + .base-date-picker {
        .mgt10;
      }
    }
    &.date {
      .base-date-picker .el-input {
        width: 150px;
      }
    }
  }
  .box-divider {
    padding: 0 5px;
    margin-top: 3px;
    color: @color-text-regular;
  }
  .base-date-picker {
    .el-input {
      width: 200px;
    }
  }
</style>
